package org.xmap.ui;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;

import org.xmap.app.App;
import org.xmap.app.Env;
import org.xmap.debug.DebugStore;
import org.xmap.debug.DebugView;
import org.xmap.ui.base.Event;

/**
 * <p>界面管理类具有唯一的实例，继承自Canvas，该类是UI的核心类，处理所有的事件响应，并把所有的事件派发给当前视图对象。</p>
 * @author Zealot
 * @version 1.0
 */
public final class ViewManager extends Canvas implements Runnable {

    /**
     * 全局的ViewManager实例。
     */
    public static final ViewManager instance = new ViewManager();
    /**
     * 对唯一App对象的引用，方便访问app的函数。
     */
    private App app;
    /**
     * midp的Display对象的引用，方便访问。
     */
    private Display disp;
    /**
     * 当前视图。
     */
    private View currentView;
    /**
     * 是否处于运行状态。
     */
    private boolean runFlag;
    /**
     * 画布是否在前台。
     */
    private boolean canvasOnForeground;
    /**
     * 最近一次按键按下的代码。
     */
    private int lastKeyCode;
    /**
     * 首次按键按下的时间。
     */
    private long firstKeyPressedTime;
    /**
     * 最近一次按键按下的时间。
     */
    private long lastKeyRepeatedTime;
    /**
     * 在一个循环中，是否已经接受了触笔拖动事件，因为触笔拖动事件一旦产生，数量非常多，所以在一个循环周期，只处理一个触笔拖动事件。
     */
    private volatile boolean isPointerDrag;
    /**
     * 事件队列，存放所有的事件，然后等待视图处理。
     */
    private Vector events;
    /**
     * 是否需要重新刷新屏幕。
     */
    private boolean needRepaint;

    /**
     * 构造函数，初始化所有的成员。
     */
    private ViewManager() {
        this.setFullScreenMode(true);
        Env.instance.screenWidth = this.getWidth();
        Env.instance.screenHeight = this.getHeight();
        Env.instance.hasPointerEvents = this.hasPointerEvents();
        this.app = null;
        this.disp = null;
        this.currentView = null;
        this.runFlag = false;
        this.canvasOnForeground = true;
        this.lastKeyCode = 0;
        this.lastKeyRepeatedTime = 0;
        this.isPointerDrag = false;
        this.events = new Vector(32);
        this.needRepaint = true;
    }

    /**
     * 输出信息到调试界面，如果关闭了调试，则什么都不输出。
     * @param msg String 输出的字符串信息。
     */
    public void debugPrint(String msg) {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                DebugView.instance.addText(msg);
            }
        }
    }

    /**
     * 清空调试界面的信息。
     */
    public void resetPrint() {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                DebugView.instance.resetText();
            }
        }
    }

    /**
     * 输出信息到持久存储，如果关闭了调试，则什么都不写入。
     * @param msg String 输出的字符串信息。
     */
    public void debugStore(String msg) {
        if (Env.instance.debug) {
            if (DebugStore.instance != null) {
                DebugStore.instance.addText(msg);
            }
        }
    }

    /**
     * 打开或关闭调试开关。
     * @param show boolean 调试开关状态。
     */
    public void setShowDebug(boolean show) {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                DebugView.instance.showDebug = show;
                ViewManager.instance.requestRepaint();
            }
        }
    }

    /**
     * 获得调试开关状态。
     * @return boolean 调试开关状态。
     */
    public boolean getShowDebug() {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                return DebugView.instance.showDebug;
            }
        }
        return false;
    }

    /**
     * 打开或关闭内存开关。
     * @param show boolean 内存开关状态。
     */
    public void setShowMemory(boolean show) {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                DebugView.instance.showMemory = show;
            }
            ViewManager.instance.requestRepaint();
        }
    }

    /**
     * 获得内存开关状态。
     * @return boolean 内存开关状态。
     */
    public boolean getShowMomery() {
        if (Env.instance.debug) {
            if (DebugView.instance != null) {
                return DebugView.instance.showMemory;
            }
        }
        return false;
    }

    /**
     * 启动界面管理器，返回是否启动成功，再次启动处于启动状态的界面管理器将返回失败。
     * @param app App 应用程序实例。
     * @return boolean 返回是否启动成功。
     */
    public boolean start(App app) {
        if (app != null && this.runFlag == false) {
            this.app = app;
            this.disp = Display.getDisplay(this.app);
            this.runFlag = true;
            new Thread(this).start();
            return true;
        }
        return false;
    }

    /**
     * 停止界面管理器。
     */
    public void stop() {
        this.runFlag = false;
    }

    /**
     * 获得midp的display句柄。
     * @return Display midp的display句柄。
     */
    public Display getDisplay() {
        return this.disp;
    }

    /**
     * 把当前程序切换到后台。
     * @return boolean 切换结果。
     */
    public boolean toBackground() {
        if (this.disp != null) {
            this.disp.setCurrent(null);
            return true;
        }
        return false;
    }

    /**
     * 把当前程序切换到前台。
     * @return boolean 切换结果。
     */
    public boolean toForeground() {
        if (this.disp != null) {
            this.disp.setCurrent(this.disp.getCurrent());
            return true;
        }
        return false;
    }

    /**
     * 显示指定的界面对象。
     * @param obj Object 界面对象。
     * @return boolean 返回设置结果。
     */
    public boolean setView(Object obj) {
        return this.setView(obj, View.ANIMATION_NONE);
    }

    /**
     * 显示指定的界面对象。
     * @param obj Object 界面对象。
     * @param animationState int 界面进入的方式，1为从左到右，-1为从右到左。
     * @return boolean 返回设置结果。
     */
    public boolean setView(Object obj, int animationState) {
        if (obj == null) {
            this.currentView = null;
            return true;
        } else {
            if (obj instanceof View) {
                this.currentView = (View) obj;
                this.currentView.setAnimation(animationState);
                if (this.disp != null) {
                    this.disp.setCurrent(this);
                    this.canvasOnForeground = true; // 加上这句是因为在模拟器上，有时候收不到通知
                }
                this.requestRepaint();
                return true;
            } else if (obj instanceof Displayable) {
                if (this.disp != null) {
                    this.disp.setCurrent((Displayable) obj);
                    this.canvasOnForeground = false; // 加上这句是因为在模拟器上，有时候收不到通知
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    }

    /**
     * 获得当前显示的界面对象。
     * @return View 当前显示的界面对象。
     */
    public View getCurView() {
        return this.currentView;
    }

    /**
     * 请求重绘屏幕，大部分情况都会重绘屏幕，比如按键事件产生之后一般都会重绘屏幕。
     */
    public void requestRepaint() {
        this.needRepaint = true;
    }

    /**
     * midp的按键按下回调函数。
     * @param keyCode int 按键键值。
     */
    protected void keyPressed(int keyCode) {
        this.events.addElement(new Event(Event.KEY_PRESSED, keyCode, 0));
        if (Env.instance.debug) {
//            if (keyCode == '0') {
//                if (DebugView.instance.showDebug) {
//                    DebugView.instance.resetText();
//                }
//                System.gc();
//            } else if (keyCode == Canvas.KEY_STAR) {
//                this.setShowMemory(!DebugView.instance.showMemory);
//            } else if (keyCode == Canvas.KEY_POUND) {
//                this.setShowDebug(!DebugView.instance.showDebug);
//            }
//
            if (keyCode == '5') {
                if (DebugView.instance.showDebug) {
                    DebugView.instance.resetText();
                }
                System.gc();
            } else if (keyCode == Canvas.KEY_NUM4) {
                this.setShowMemory(!DebugView.instance.showMemory);
            } else if (keyCode == Canvas.KEY_NUM6) {
                this.setShowDebug(!DebugView.instance.showDebug);
            }
        }
    }

    /**
     * midp的按键重复回调函数，该函数已经被抛弃，因为视图自己实现按键重复的事件响应。
     * @param keyCode int 按键键值。
     */
    protected void keyRepeated(int keyCode) {
        this.events.addElement(new Event(Event.KEY_REPEATED, keyCode, 0));
    }

    /**
     * midp的按键释放回调函数。
     * @param keyCode int 按键键值。
     */
    protected void keyReleased(int keyCode) {
        this.events.addElement(new Event(Event.KEY_RELEASED, keyCode, 0));
    }

    /**
     * midp的触笔按下回调函数。
     * @param x int 触笔横坐标。
     * @param y int 触笔纵坐标。
     */
    protected void pointerPressed(int x, int y) {
        this.events.addElement(new Event(Event.POINTER_PRESSED, x, y));
        if (Env.instance.debug) {
            if (x < 50 && y < 10) {
                this.setShowDebug(!DebugView.instance.showDebug);
            } else if (x > 50 && x < 100 && y < 10) {
                this.setShowMemory(!DebugView.instance.showMemory);
            } else if (x > 100 && x < 150 && y < 10) {
                if (DebugView.instance.showDebug) {
                    DebugView.instance.resetText();
                }
            } else if (x > 150 && x < 200 && y < 10) {
                System.gc();
            }
        }
    }

    /**
     * midp的触笔拖动回调函数。
     * @param x int 触笔横坐标。
     * @param y int 触笔纵坐标。
     */
    protected void pointerDragged(int x, int y) {
        if (!this.isPointerDrag) { // 每帧最多有一个Drag事件
            this.events.addElement(new Event(Event.POINTER_DRAGGED, x, y));
            this.isPointerDrag = true;
        }
    }

    /**
     * midp的触笔释放回调函数。
     * @param x int 触笔横坐标。
     * @param y int 触笔纵坐标。
     */
    protected void pointerReleased(int x, int y) {
        this.events.addElement(new Event(Event.POINTER_RELEASED, x, y));
    }

    /**
     * 视图管理器的绘制函数。
     * @param g Graphics 图形句柄。
     */
    protected void paint(Graphics g) {
        g.setFont(Env.instance.defaultFont);
        if (this.currentView != null) {
            this.drawView(g, this.currentView);
        }
        if (Env.instance.debug) {
            if (DebugView.instance.showDebug) {
                this.drawView(g, DebugView.instance);
            }
            if (DebugView.instance.showMemory) {
                g.setColor(0xffff0000);
                g.drawString("total/used/free", 0, 0, Graphics.LEFT | Graphics.TOP);
                long total = Runtime.getRuntime().totalMemory();
                long free = Runtime.getRuntime().freeMemory();
                g.drawString(total + "/" + (total - free) + "/" + free, 0, 15, Graphics.LEFT | Graphics.TOP);
                if (DebugView.instance.refreshCount > 10) {
                    StringBuffer sb = new StringBuffer(String.valueOf(
                            DebugView.instance.refreshCount * 1000000 /
                            (System.currentTimeMillis() - DebugView.instance.refreshTime)));
                    DebugView.instance.refreshTime = System.currentTimeMillis();
                    DebugView.instance.refreshCount = 0;
                    int zeroCount = 4 - sb.length();
                    for (int i = 0; i < zeroCount; i++) {
                        sb.insert(0, '0');
                    }
                    sb.insert(sb.length() - 3, '.');
                    DebugView.instance.fps = "fps:" + sb.toString();
                }
                g.drawString(DebugView.instance.fps, 0, 30, Graphics.LEFT | Graphics.TOP);
            }
        }
    }

    /**
     * midp的Canvas被切换出去的回调函数。
     */
    protected void hideNotify() {
        this.events.addElement(new Event(Event.HIDE_NOTIFY, 0, 0));
    }

    /**
     * midp的Canvas被切换进来的回调函数。
     */
    protected void showNotify() {
        this.events.addElement(new Event(Event.SHOW_NOTIFY, 0, 0));
    }

    /**
     * midp的Canvas的区域被修改的回调函数。
     * @param width 新的绘制区域宽度。
     * @param height 新的绘制区域高度。
     */
    protected void sizeChanged(int width, int height) {
        this.events.addElement(new Event(Event.SIZE_CHANGED, width, height));
    }

    /**
     * 视图管理器的循环函数。
     */
    public void run() {
        long startTime = System.currentTimeMillis();
        while (this.runFlag) {
            try {
                this.updateManager();
                int sleepInterval = Env.instance.updateCycle - (int) (System.currentTimeMillis() - startTime);
                startTime = System.currentTimeMillis();
                if (sleepInterval > 0) {
                    Thread.sleep(sleepInterval);
                    startTime += sleepInterval;
                }
                if (Env.instance.debug) {
                    DebugView.instance.refreshCount++;
                }
            } catch (Exception e) {
                ViewManager.instance.debugPrint("error:" + e.getMessage());
            }
        }
        this.app = null;
    }

    /**
     * 视图管理器的更新函数。
     */
    private void updateManager() {
        Env.instance.updateTime();
        //        if (!events.isEmpty()) { // process event
        //            for (int i = 0; i < this.events.size(); i++) {
        while (this.events.size() > 0) {
            Event ev = (Event)this.events.elementAt(0);
            this.events.removeElementAt(0);
            if (Env.instance.debug && DebugView.instance.showDebug) {
                this.processEvent(ev, DebugView.instance);
            } else {
                this.processEvent(ev, this.currentView);
            }
        }
        //            this.events.removeAllElements();
        //        }
        this.isPointerDrag = false;
        if (this.lastKeyCode != 0) {
            int keyLasting = (int) Math.abs(System.currentTimeMillis() - this.firstKeyPressedTime);
            if (keyLasting > Env.instance.firstRepeatedInterval) {
                int keyInterval = Math.abs(((int) (System.currentTimeMillis() - this.lastKeyRepeatedTime)));
                if (keyInterval > Env.instance.keyRepeatedInterval) {
//                    this.events.addElement(new Event(Event.KEY_REPEATED, this.lastKeyCode, 0));
                    this.lastKeyRepeatedTime = System.currentTimeMillis();
                }
            }
        }
        if (Env.instance.debug) {
            if (DebugView.instance.showDebug) {
                ((View) DebugView.instance).baseUpdate();
            }
            if (DebugView.instance.showMemory) {
                ViewManager.instance.requestRepaint();
            }
        }
        if (this.currentView != null) {
            this.currentView.baseUpdate();
        }
        if (this.app != null) { // update
            this.app.update();
        }
        if (this.canvasOnForeground && this.needRepaint) { // repaint
//            System.out.println("=====repaint");
            this.needRepaint = false;
            this.repaint();
            this.serviceRepaints();
        }
    }

    /**
     * 视图管理器把事件派发给视图处理。
     * @param ev Event 事件。
     * @param v View 视图。
     */
    private void processEvent(Event ev, View v) {
        switch (ev.type) {
        case Event.KEY_PRESSED:
        case Event.KEY_REPEATED:
        case Event.KEY_RELEASED:
            try {
                ev.param2 = this.getGameAction(ev.param1); // 有些手机，比如k700，对某些键值的转换会产生一个异常，捕获异常后，不做处理
            } catch (Exception e) {
            }
            if (ev.type == Event.KEY_PRESSED) {
                this.lastKeyCode = ev.param1;
                this.firstKeyPressedTime = System.currentTimeMillis();
                if (v != null) {
                    v.baseKeyEvent(true, ev.param1, ev.param2);
                }
            } else if (ev.type == Event.KEY_REPEATED) {
                if (v != null) {
                    v.baseKeyEvent(true, ev.param1, ev.param2);
                }
            } else if (ev.type == Event.KEY_RELEASED) {
                this.lastKeyCode = 0;
                if (v != null) {
                    v.baseKeyEvent(false, ev.param1, ev.param2);
                }
            }
            if (this.app != null) {
                this.app.keyActive();
            }
            break;
        case Event.POINTER_PRESSED:
        case Event.POINTER_RELEASED:
        case Event.POINTER_DRAGGED:
            if (v != null) {
                v.basePointerEvent(ev);
            }
            if (this.app != null) {
                this.app.pointerActive();
            }
            break;
        case Event.SHOW_NOTIFY:
            this.canvasOnForeground = true;
            this.requestRepaint();
            if (this.app != null) {
                this.app.showNotify();
            }
            break;
        case Event.HIDE_NOTIFY:

            // 由于按键事件导致菜单响应，如果响应代码中有联网提示等导致canvas被切换出去，那么按键的释放事件将获得不到
            // 加上这行代码，可以避免由于这种情况产生的按键事件重复bug。
            this.lastKeyCode = 0;
            this.canvasOnForeground = false;
            if (this.app != null) {
                this.app.hideNotify();
            }
            break;
        case Event.SIZE_CHANGED:
            this.requestRepaint();
            Env.instance.screenWidth = ev.param1;
            Env.instance.screenHeight = ev.param2;
            break;
        }
    }

    /**
     * 绘制视图的函数。
     * @param g Graphics 图形句柄。
     * @param v View 视图。
     */
    private void drawView(Graphics g, View v) {
        if (g != null && v != null) {
            g.translate(v.getRect().leftUp.x, v.getRect().leftUp.y);
            int clipX = g.getClipX();
            int clipY = g.getClipY();
            int clipW = g.getClipWidth();
            int clipH = g.getClipHeight();
            g.setClip(0, 0, v.getRect().width, v.getRect().height);
            v.baseDraw(g);
            g.setClip(clipX, clipY, clipW, clipH);
            g.translate( -v.getRect().leftUp.x, -v.getRect().leftUp.y);
        }
    }
}
